/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package org.grnt.dbcommon;

import org.grnt.dbcommon.DataControler.DataControlException;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import javax.sql.DataSource;
import org.grnt.dbcommon.ResultBean.FullValuesException;

/**
 *
 * @author sayid
 * It is a control center of doing with database. There are two methods with using 
 * two interfaces ResultBean and ControlBean. The ResultBean is for "select" action.
 * The ControlBean is for a transaction doing.
 */
public class DataControler {
	private static DataSource dataSource;

	/**
	 *
	 * When this method is called and successful the DataControler class will be active.
	 * @param datasource The Resouce of database.
	 * @throws org.grnt.dbcommon.DataControler.DataControlException
	 */
	public static void registDataSource(DataSource datasource) throws DataControlException{
		if (dataSource == null)
			DataControler.dataSource = datasource;
		else
			throw new DataControlException("The data controler has been actived.");
	}

	/**
	 *
	 * Only registDataSource method has been called and successful, the DataControler is active.
	 * @return Wethere the action is sccessfule or not.
	 */
	public static boolean isActive() {
		return dataSource != null;
	}

	/**
	 *
	 * It does a query action with ResultBean object.
	 * @param rb It is ResultBean type variable. The ResultBean gives some methods
	 *  can for quering effects. one is getQueryString returns a "select" sql command.
	 * another is fullValues method witch recieve result set and do with the set.
	 * @throws org.grnt.dbcommon.DataControler.DataControlException
	 */
	public final static void query(final ResultBean rb) throws DataControlException{
		Connection connection = null;
		try{
			connection = dataSource.getConnection();
		}catch(SQLException e) {
			throw new DataControlException("can't get a correct connection.", e);
		}
		if (connection == null) {
			throw new DataControlException("the connection of result is null");
		}

		String qryStr = null;
		try{
			qryStr = rb.getQueryString();
			PreparedStatement ps =
					connection.prepareStatement(qryStr);
			ResultSet rs = ps.executeQuery();
			rb.fullValues(rs);
		} catch (FullValuesException ex) {
			throw new DataControlException(qryStr, ex);
		} catch (SQLException e) {
			throw new DataControlException(qryStr, e);
		} finally {
			try {
				connection.close();
			} catch (SQLException e) {
				throw new DataControlException(connection, e);
			}
		}
	}

	/**
	 *
	 * It does a transaction with a serial DMLs provided by ControlBean and bound
	 * some ControlBean's hook methods one is doInTransaction another is 
	 * doAfterTransaction.
	 * @param controlBean
	 * It is ControlBean type. ControlBean type gives some
	 * methods as callback functions to implement a data control transaction. 
	 * One method is give a serial dmls of a transaction. Another method is 
	 * doInTransaction witch is called befor the transaction is end. Last method
	 * is doAfterTrasaction witch is called at after end of transaction. 
	 * @return A serial result code of jdbc returned.
	 * @throws org.grnt.dbcommon.DataControler.DataControlException
	 */
	public static final List<Integer> update(final ControlBean controlBean) throws DataControlException{
		Connection connection = null;
		try{
			connection = dataSource.getConnection();
		}catch (SQLException e) {
			throw new DataControlException("Can't get a correct connection.", e);
		}
		if (connection == null) {
			throw new DataControlException("the connection of result is null.");
		}

		try {
			connection.setAutoCommit(false);
		} catch (SQLException e) {
			throw new DataControlException("Can't use transaction!", e);
		}
		ArrayList<Integer> result = new ArrayList<Integer>();
		String commandSql = null;
		try{
			for (String str: controlBean.getCommands()){
				commandSql = str;
				PreparedStatement ps = connection.prepareCall(commandSql);
				result.add(ps.executeUpdate());
			}
		}catch (SQLException e) {
			transactionClose(connection, e, TransOpt.ROLLBACK, commandSql);
			controlBean.doAfterTransaction(TransOpt.ROLLBACK);
		}

		if (controlBean.doInTransaction()) {
			transactionClose(connection, null, TransOpt.COMMIT, null);
			controlBean.doAfterTransaction(TransOpt.COMMIT);
			return result;
		} else {
			transactionClose(connection, null, TransOpt.ROLLBACK, null);
			controlBean.doAfterTransaction(TransOpt.ROLLBACK);
			throw new DataControlException(
					"The internal options can't be executed successfull");
		}
	}

	public static enum TransOpt {COMMIT, ROLLBACK}
	private final static void transactionClose(Connection cnt, SQLException expt, TransOpt opt
			, String command) throws DataControlException{

		SQLException inerExpt = expt;
		String msg = null;
		switch (opt) {
			case COMMIT:
				try{
					cnt.commit();
					opt = TransOpt.COMMIT;
				}catch(SQLException e) {
					msg = "exception happened on transaction commit.";
					opt = TransOpt.ROLLBACK;
					inerExpt = e;
				}
			case ROLLBACK:
				try{
					cnt.rollback();
					opt = TransOpt.ROLLBACK;
				} catch(SQLException e) {
					msg = "exception happened on transaction rollback.";
					inerExpt = e;
				}
		}

		try {
			cnt.close();
			if (inerExpt != null) {
				if (command != null) {
					msg = inerExpt.getMessage() + " and the command is '" + command + "'";
				}
				throw new DataControlException(msg, inerExpt);
			}
		} catch (SQLException e) {
			throw new DataControlException(cnt, e);
		}
	}
	/**
	 * It is an Exception Class which throwed by DataControler class.
	 */
	public static class DataControlException extends Exception{
		static List<Connection> inactiveConnections = new ArrayList<Connection>();
		
		public DataControlException(String msg, Throwable cause) {
			super(msg, cause);
		}
		
		public DataControlException(Throwable cause) {
			super(cause);
		}
		
		public DataControlException(String message) {
			super(message);
		}
		
		public DataControlException() {
		}
		
		public DataControlException(Connection cnt, Throwable cause) {
			super("A connection can't be operated and has been frozen", cause);
			inactiveConnections.add(cnt);
		}
		
		public static List<Connection> getinactiveConnections() {
			return inactiveConnections;
		}
	}
}